using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;
using ChessKit.Common;
using System.Linq;

namespace ChessKit.Ui
{
  public class BoardMouseInput : Behavior<UIElement>
  {
    #region ' Settable Public Properties '

    #region ' Disposition : IDispositionProvider (dependency property) '

    public static readonly DependencyProperty DispositionProperty =
      DependencyProperty.Register("Disposition", typeof (IDispositionProvider), typeof (BoardMouseInput),
                                  new PropertyMetadata(default(IDispositionProvider)));

    public IDispositionProvider Disposition
    {
      get { return (IDispositionProvider) GetValue(DispositionProperty); }
      set { SetValue(DispositionProperty, value); }
    }

    #endregion

    #region ' GameControl : GameControlProperty (dependency property) '

    public static readonly DependencyProperty GameControlProperty =
      DependencyProperty.Register("GameControl", typeof (IGameControl), typeof (BoardMouseInput),
                                  new PropertyMetadata(default(IGameControl)));

    public IGameControl GameControl
    {
      get { return (IGameControl) GetValue(GameControlProperty); }
      set { SetValue(GameControlProperty, value); }
    }

    #endregion

    #region ' LegalMoves : ILegalMovesProvider (dependency property) '

    public static readonly DependencyProperty LegalMovesProperty =
      DependencyProperty.Register("LegalMoves", typeof (ILegalMovesProvider), typeof (BoardMouseInput),
                                  new PropertyMetadata(default(ILegalMovesProvider)));

    public ILegalMovesProvider LegalMoves
    {
      get { return (ILegalMovesProvider) GetValue(LegalMovesProperty); }
      set { SetValue(LegalMovesProperty, value); }
    }

    #endregion

    #region ' HoverPieceTemplate : ControlTemplate (dependency property) '

    public static readonly DependencyProperty HoverPieceTemplateProperty =
      DependencyProperty.Register("HoverPieceTemplate",
                                  typeof (ControlTemplate), typeof (BoardMouseInput),
                                  new PropertyMetadata(default(ControlTemplate)));

    public ControlTemplate HoverPieceTemplate
    {
      get { return (ControlTemplate) GetValue(HoverPieceTemplateProperty); }
      set { SetValue(HoverPieceTemplateProperty, value); }
    }

    #endregion

    public IPromotionControlProvider Provider { get; set; }
    public CanMoveMode CanMove { get; set; }

    #endregion

    protected override void OnAttached()
    {
      DragDropBehavior.AddDraggingHandler(AssociatedObject, OnDragging);
      DragDropBehavior.AddDragHandler(AssociatedObject, OnDrag);
      DragDropBehavior.AddDropHandler(AssociatedObject, OnDrop);
    }
    protected override void OnDetaching()
    {
      DragDropBehavior.RemoveDraggingHandler(AssociatedObject, OnDragging);
      DragDropBehavior.RemoveDragHandler(AssociatedObject, OnDrag);
      DragDropBehavior.RemoveDropHandler(AssociatedObject, OnDrop);
    }

    #region ' Possible Moves '

    private readonly HashSet<MoveTarget> _possibleMoves = new HashSet<MoveTarget>();
    private readonly CollectionColorizer _possibleMovesColorizer =
      new CollectionColorizer(CellStyles.IsPossibleMoveTargetProperty);

    public CollectionColorizer PossibleMovesColorizer
    {
      get { return _possibleMovesColorizer; }
    }

    private void SetPossibleMoves(IEnumerable<MoveTarget> boardLocations)
    {
      _possibleMoves.Clear();
      _possibleMovesColorizer.Locations.Clear();
      foreach (var l in boardLocations)
      {
        _possibleMoves.Add(l);
        _possibleMovesColorizer.Locations.Add(l.Location);
      }
      _possibleMovesColorizer.Refresh();
    }

    #endregion

    private void OnDragging(object sender, DraggingEventArgs e)
    {
      var p = BoardPanel.GetLocation((DependencyObject)e.OriginalSource);
      switch (CanMove)
      {
        case CanMoveMode.Anytime:
          e.CanDrag = true;
          break;
        case CanMoveMode.LegitimateMoves:
          e.CanDrag = LegalMoves.GetLegalMoves(p).Any();
          break;
        case CanMoveMode.SideOnMove:
          e.CanDrag = GameControl.SideOnMove ==
                      Disposition.GetPiece(p).Color;
          break;
      }
    }
    private void OnDrag(object sender, DragEventArgs e)
    {
      var piece = (FrameworkElement)e.OriginalSource;
      var panel = (Panel)piece.Parent;
      panel.Children.Remove(piece);

      SetPossibleMoves(LegalMoves.GetLegalMoves(BoardPanel.GetLocation(piece)));

      e.FailToDrop = () =>
        {
          panel.Children.Add(piece);
          SetPossibleMoves(Enumerable.Empty<MoveTarget>());
        };
    }
    private void OnDrop(object sender, DropEventArgs e)
    {
      var cell = (FrameworkElement)e.OriginalSource;
      var piece = e.DraggedObject;
      var from = BoardPanel.GetLocation(piece);
      var to = BoardPanel.GetLocation(cell);
      var panel = (Panel)cell.Parent;
      var move = _possibleMoves.FirstOrDefault(m => m.Location == to);

      SetPossibleMoves(Enumerable.Empty<MoveTarget>());

      if (move.Location.Area != BoardArea.Cell)
      {
        panel.Children.Add(piece);
      }
      else if (!move.RequiresPromotion)
      {
        GameControl.Move(from, to);
        BoardPanel.SetLocation(piece, to);
        panel.Children.Add(piece);
      }
      else if (Provider == null)
      {
        GameControl.Move(from, to);
        BoardPanel.SetLocation(piece, to);
      }
      else ShowPromotionPopup(piece, from, to);
    }

    #region ' Implementation '

    private void ShowPromotionPopup(DependencyObject piece, BoardLocation from, BoardLocation to)
    {
      var promotionControl = Provider.CreateControl(Disposition.GetPiece(from).Color);
      var popup = new Popup
                    {
                      Placement = PlacementMode.MousePoint,
                      Child = (UIElement) promotionControl,
                      IsOpen = true,
                    };
      promotionControl.ChoiceDone += (s, x) =>
                                       {
                                         GameControl.Move(from, to, promotionControl.PromotionChoice);
                                         BoardPanel.SetLocation(piece, to);
                                         popup.IsOpen = false;
                                       };
    }

    #endregion
  }
}